#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/wait.h>
#define SIZE 512
#define ZERO '\0'
#define NUM 32
#define SEP " "
#define skipspace(usercommand,p) do{while(1){if(isspace(usercommand[pos])) pos++;else break;}}while(0)

#define NONE_REDIR 0
#define OUT_REDIR 1
#define IN_REDIR 2
#define APP_REDIR 3

int redir_type = NONE_REDIR;
char* filename = NULL;

const char* getusername()
{
  const char* name = getenv("USER");
  if(name == NULL) return "none";
  return name;
}

const char* gethostname()
{
  const char* hostname = getenv("HOSTNAME");
  if(hostname == NULL) return "none";
  return hostname;
}

const char* getcwd()
{
  const char* cwd = getenv("PWD");
  if(cwd == NULL) return "none";
  return cwd;
}

void makecommandline()
{
  char line[SIZE];
  const char* username = getusername();
  const char* hostname = gethostname();
  const char* cwd = getcwd();
  
  snprintf(line, sizeof(line), "[%s@%s %s]* ", username, hostname, cwd);
  printf("%s", line);
  fflush(stdout);
}

int getusercommand(char command[], size_t n)
{

  char *s = fgets(command, n, stdin);
  command[strlen(command) - 1] = ZERO;
  if(s == NULL) return -1;

  return strlen(command);
}

char *gArgv[NUM];

void splitcommand(char command[], int  n)
{ 
  //ls - a -l -n
  gArgv[0] = strtok(command, SEP);
  int index = 1;

  while((gArgv[index++] = strtok(NULL, SEP)));

}

int lastcode = 0;


void executecommand()
{
  pid_t id = fork();
  if(id < 0) exit(1);
  else if(id == 0)
  {
    //重定向设置
    if(filename != NULL){
      if(redir_type == IN_REDIR)
      {
        int fd = open(filename, O_RDWR);
        dup2(fd, 0);
      }
      else if(redir_type == OUT_REDIR)
      {
        int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
        dup2(fd, 1);
      }
      else if(redir_type == APP_REDIR)
      {

        int fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, 0666);
        dup2(fd, 1);
      }
    }
    //child
    
    execvp(gArgv[0], gArgv);
    exit(errno);
  }
  else
  {
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if(rid > 0)
    {
      lastcode = WEXITSTATUS(status);
      if(lastcode != 0) printf("%s: %s : %d\n",gArgv[0], strerror(lastcode), lastcode);
    }
  }
}

const char* gethome()
{
  const char* home = getenv("HOME");
  if(home == NULL) return "/";  
  return home;
}

char cwd[SIZE];

void Cd()
{
  const char* path = gArgv[1];
  if(path == NULL) path = gethome();
   
  chdir(path);

  char temp[SIZE];
  getcwd(temp, sizeof(temp));
  snprintf(cwd, sizeof(cwd), "PWD=%s", temp);
  putenv(cwd);
}

int checkbuildir()
{
  int yes = 0;
  const char* enter_cmd = gArgv[0];
  if(strcmp(enter_cmd, "cd") == 0)
  {
    yes = 1;
    Cd();
  }
  return yes;
}

void checkredir(char usercommand[])
{
  int pos = 0;
  int end = strlen(usercommand);
  while(pos < end)
  {
    if(usercommand[pos] == '>')
    {
      if(usercommand[pos + 1] == '>')
      {
        usercommand[pos++] = 0;
        pos++;
        redir_type = APP_REDIR;
        skipspace(usercommand,pos);
        filename = usercommand + pos;
      }
      else{
        usercommand[pos++] = 0;
        redir_type = OUT_REDIR;
        skipspace(usercommand, pos);
        filename = usercommand + pos;
      }
      printf("%d\n", pos);
    }
    else if(usercommand[pos] == '<')
    {
      usercommand[pos] = 0;
      redir_type = IN_REDIR;
      pos++;
      skipspace(usercommand, pos);
      filename = usercommand + pos;
    }
    else
    {
      pos++;
    }
  }
}

int main()
{
  int quit = 1;
  while(quit){
    //初始化
    redir_type = NONE_REDIR;
    filename = NULL;
    //自己输出一个命令行
    makecommandline();

    //获取用户命令
    char usercommand[SIZE];
    getusercommand(usercommand, sizeof(usercommand));
    
    checkredir(usercommand);

    //命令行字符分割，分割符是空格
    splitcommand(usercommand, sizeof(usercommand));

    int n = checkbuildir();
    if(n) continue;

    //检查重定向
   // printf("%s\n", filename);
    //执行命令
    executecommand();
  }
  return 0;
}
